//++
//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
// Module Name:
//
//  NotificationChannel.cs
//    
// Abstract:
//
//  This module implements the NotificationChannel and NotificationChannelType classes. These are used to 
//  describe a notification channel for a reminder / notification.
//  
//--

using System;
using System.Diagnostics;
using System.Globalization;
using System.Text;
using Microsoft.OfficeCommunicationsServer.Applications.Common;

namespace Microsoft.EnhancedReminders
{
    /// <summary>
    /// Represents a notification channel whose details are stored by OCS (e.g. mobile phone number).
    /// </summary>
    [Serializable()]
    public class NotificationChannel
    {
        /// <summary>
        /// Format of the channel description string logged by the scheduler.
        /// </summary>
        public const string FormatString = "Channel id={0}, enabled={1}, type={2}, custom={3}";

        /// <summary>
        /// The communicator channel identifier.
        /// </summary>
        public static readonly Guid CommunicatorId = new Guid("95895D6F-5AF3-485d-8A8B-13D9401A1421");
        
        /// <summary>
        /// The mobile phone channel identifier.
        /// </summary>
        public static readonly Guid MobilePhoneId = new Guid("907AA967-913A-4b6a-A460-647FA72BBFA8");
        
        /// <summary>
        /// The work phone channel identifier.
        /// </summary>
        public static readonly Guid WorkPhoneId = new Guid("DAA127C3-EC03-44ac-B1E2-11D30A0743A4");
        
        /// <summary>
        /// The home phone channel identifier.
        /// </summary>
        public static readonly Guid HomePhoneId = new Guid("FADEA332-5CB1-4fe2-B91A-DEF7EE6FAD11");
        
        /// <summary>
        /// The "other" phone channel identifier.
        /// </summary>
        public static readonly Guid OtherPhoneId = new Guid("0908B0FF-DA72-4a63-B92C-BF422EE8983B");


        private static bool _doNotPromptUserOnDelete;

        /// <summary>
        /// Unique identifier for the channel.  This determines where channel data is to be
        /// stored for OCS channels (e.g. home phone vs mobile phone).  For custom channels
        /// the id allows the channel to be correctly referenced.
        /// </summary>
        protected Guid _id;

        private string _userDefinedValue;
        private string _userDefinedName;
        private bool _isEnabled = true;

        /// <summary>
        /// Default ctor. The channel type defaults to "Communicator".
        /// </summary>
        public NotificationChannel() : this(NotificationChannelType.Communicator)
        {
        }

        /// <summary>
        /// ctor.
        /// </summary>
        /// <param name="notificationChannelType">Type of channel</param>
        public NotificationChannel(NotificationChannelType notificationChannelType)
        {
            NotificationChannelType = notificationChannelType;
        }

        /// <summary>
        /// ctor.  Backwards compatibility only - requires channel type to be communicator or phone.
        /// </summary>
        /// <param name="notificationChannelType">Type of channel</param>
        /// <param name="channelName">A name for the channel</param>
        /// <param name="channelValue">The address of the channel</param>
        public NotificationChannel(NotificationChannelType notificationChannelType, string channelName, string channelValue) :
            this (notificationChannelType)
        {
            _userDefinedName = channelName;
            _userDefinedValue = channelValue;
        }

        /// <summary>
        /// ctor.
        /// </summary>
        /// <param name="id">The channel type id</param>
        /// <param name="channelName">A name for the channel</param>
        /// <param name="channelValue">The address of the channel</param>
        public NotificationChannel(Guid id, string channelName, string channelValue)
        {
            Debug.Assert((this.GetType() == typeof(NotificationCustomChannel) ^
                          (id == CommunicatorId || id == MobilePhoneId || id == WorkPhoneId || id == HomePhoneId || id == OtherPhoneId)),
                          "id must be pre-defined for NotificationChannel and a newly assigned one for a custom channel");

            _id = id;
            _userDefinedName = channelName;
            _userDefinedValue = channelValue;
        }

        /// <summary>
        /// Copy ctor.  Used by the Copy() method only.
        /// </summary>
        /// <param name="channel">The NotificationChannel instance to take a copy of.</param>
        protected NotificationChannel(NotificationChannel channel)
        {
            Debug.Assert(channel != null, "channel != null");

            _id = channel._id;
            _isEnabled = channel._isEnabled;
            _userDefinedName = channel._userDefinedName;
            _userDefinedValue = channel._userDefinedValue;
        }

        /// <summary>
        /// Creates a copy of the appropriate type for the NotificationChannel instance.
        /// </summary>
        public virtual NotificationChannel Copy()
        {
            return new NotificationChannel(this);
        }

        /// <summary>
        /// Flag indicating if user should be prompted when deleting channels.
        /// </summary>
        public static bool DoNotPromptUserOnDelete
        {
            get
            {
                return _doNotPromptUserOnDelete;
            }
            set
            {
                _doNotPromptUserOnDelete = value;
            }
        }

        /// <summary>
        /// Type of channel.
        /// </summary>
        public virtual NotificationChannelType NotificationChannelType
        {
            get
            {
                return _id == CommunicatorId ? NotificationChannelType.Communicator : NotificationChannelType.Phone;
            }
            set
            {
                // For backwards compatability only.

                Debug.Assert(value == NotificationChannelType.Communicator ||
                             value == NotificationChannelType.Phone,
                             "OCS only supports Communicator or Phone channels");

                _id = value == NotificationChannelType.Communicator ? CommunicatorId : MobilePhoneId;
            }
        }

        /// <summary>
        /// Unique identifier for the channel.  Identifies which OCS channel (e.g. mobile
        /// phone) or custom channel is being referenced.
        /// </summary>
        public Guid Id
        {
            get
            {
                Debug.Assert(_id != Guid.Empty, "A unique ID should have been assigned.");

                return _id;
            }
            set // Used for testing purposes only.
            {
                _id = value;
            }
        }

        /// <summary>
        /// User-defined value for this channel.
        /// </summary>
        public string UserDefinedValue
        {
            get
            {
                return _userDefinedValue;
            }
            set
            {
                _userDefinedValue = value;
            }
        }

        /// <summary>
        /// User-defined name for this channel
        /// </summary>
        public string UserDefinedName
        {
            get
            {
                return _userDefinedName;
            }
            set
            {
                _userDefinedName = value;
            }
        }

        /// <summary>
        /// Whether or not the channel is enabled. A disabled channel should
        /// not be used for delivery.
        /// </summary>
        public bool IsEnabled
        {
            get
            {
                return _isEnabled;
            }
            set
            {
                _isEnabled = value;
            }
        }

        /// <summary>
        /// True if the channel can be used to join a conference or live meeting; false otherwise.
        /// </summary>
        public bool AllowsJoinNow
        {
            get
            {
                return NotificationChannelType != NotificationChannelType.SMS;
            }
        }

        /// <summary>
        /// Returns a sanitized version of UserDefinedValue if the notification channel
        /// type is one where the UI allows users to add extraneous characters (e.g. phone numbers).
        /// </summary>
        public string Value
        {
            get
            {
                if (NotificationChannelType == NotificationChannelType.Phone)
                {
                    StringBuilder cleanNumber = new StringBuilder();

                    foreach (char character in _userDefinedValue)
                    {
                        int digit;

                        if (int.TryParse(character.ToString(), out digit))
                        {
                            cleanNumber.Append(digit);
                        }
                    }

                    return string.Format(CultureInfo.InvariantCulture, "{0}+{1}", Constants.TelPrefix, cleanNumber.ToString());
                }
                else if (NotificationChannelType == NotificationChannelType.Communicator)
                {
                    return string.Format(CultureInfo.InvariantCulture, "{0}{1}", Constants.SipPrefix, _userDefinedValue);
                }
                else
                {
                    return _userDefinedValue;
                }
            }
        }

        /// <summary>
        /// Log the channel info to the verbose trace logs.
        /// </summary>
        /// <param name="context">The logging context.</param>
        internal void LogTraceInfo(Context context)
        {
            Debug.Assert(context != null, "context");

            Logger.WriteTraceVerbose(context,
                                     FormatString,
                                     Id,
                                     IsEnabled,
                                     NotificationChannelType,
                                     this is NotificationCustomChannel);
        }

        /// <summary>
        /// Produces a string representation of this object.
        /// </summary>
        /// <returns>
        /// The string representation of this object.
        /// </returns>
        /// <remarks>
        /// This is used in logging.
        /// </remarks>
        public override string ToString()
        {
            if (!string.IsNullOrEmpty(_userDefinedValue))
            {
                return NotificationChannelType.ToString() + " (" + _userDefinedValue + ")";
            }

            return NotificationChannelType.ToString();
        }

        /// <summary>
        /// Test for equality.
        /// </summary>
        public override bool Equals(object obj)
        {
            NotificationChannel channel = obj as NotificationChannel;

            if (channel == null)
            {
                return base.Equals(obj);
            }

            return Id == channel.Id &&
                NotificationChannelType == channel.NotificationChannelType &&
                _userDefinedName == channel._userDefinedName &&
                _userDefinedValue == channel._userDefinedValue &&
                _isEnabled == channel._isEnabled;
        }

        /// <summary>
        /// Overriding Equals() requires us to override GetHashCode().
        /// </summary>
        public override int GetHashCode()
        {
            return base.GetHashCode();
        }
    }
}
